package com.bilibili.api.aspect;

import com.bilibili.api.support.UserSupport;
import com.bilibili.domain.annotation.ApiLimitedRole;
import com.bilibili.domain.entity.AuthRole;
import com.bilibili.domain.entity.UserRole;
import com.bilibili.domain.exception.ConditionException;
import com.bilibili.service.AuthRoleService;
import com.bilibili.service.UserRoleService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * projectName:com.bilibili.api.aspect
 *
 * @author 飞
 * @create by 2024/6/1117:04
 * description:
 */
@Aspect
@Order(1)
@Component
public class ApiLimitedRoleAspect {

    @Resource
    private UserSupport userSupport;

    @Resource
    private UserRoleService userRoleService;
    @Resource
    private AuthRoleService authRoleService;

    @Pointcut("@annotation(com.bilibili.domain.annotation.ApiLimitedRole)")
    public void check(){

    }

    /**
     * 在目标方法执行前添加注解，用于检查当前用户是否具有执行该方法所需的权限。
     * 使用AOP（面向切面编程）中的@Before注解指定该方法为一个前置增强方法。
     * 前置增强方法会在目标方法执行前被调用，用于进行一些预处理工作。
     *
     * @param joinPoint 切点，用于获取目标方法的信息。
     * @param apiLimitedRole 注解，用于获取方法上标注的权限限制信息。
     * @throws ConditionException 如果当前用户不具备所需的权限，则抛出该异常。
     */
    @Before("check() && @annotation(apiLimitedRole)")
    public void doBefore(JoinPoint joinPoint, ApiLimitedRole apiLimitedRole){
        // 获取当前用户ID
        Long userId = userSupport.getCurrentUserId();
        // 根据用户ID获取用户角色列表
        List<UserRole> userRoleList = userRoleService.getByUserId(userId);

        // 给用户角色列表中的每个角色补充角色名称和角色代码
        // 自己加的，数据库中没有name，code字段
        for (UserRole userRole : userRoleList) {
            AuthRole authRole = authRoleService.getById(userRole.getRoleId());
            userRole.setRoleName(authRole.getName());
            userRole.setRoleCode(authRole.getCode());
        }

        // 将方法上标注的受限角色代码转换为集合
        String[] limitedRoleCodeList = apiLimitedRole.limitedRoleCodeList();
        Set<String> limitedRoleCodeSet = Arrays.stream(limitedRoleCodeList).collect(Collectors.toSet());
        // 获取当前用户拥有的角色代码集合
        Set<String> roleCodeSet = userRoleList.stream().map(UserRole::getRoleCode).collect(Collectors.toSet());
        // 判断用户是否拥有执行该方法所需的权限
        // 判断是否有交集
        roleCodeSet.retainAll(limitedRoleCodeSet);
        if (roleCodeSet.size()>0){
            throw new ConditionException("权限不足！");
        }
    }
}
